package org.angelica.aliyun.manager.timeline;

import com.alicloud.openservices.tablestore.ClientConfiguration;
import com.alicloud.openservices.tablestore.SyncClient;
import com.alicloud.openservices.tablestore.model.AlwaysRetryStrategy;
import com.alicloud.openservices.tablestore.model.search.FieldSchema;
import com.alicloud.openservices.tablestore.model.search.FieldType;
import com.alicloud.openservices.tablestore.model.search.IndexSchema;
import com.alicloud.openservices.tablestore.model.search.sort.SortOrder;
import com.alicloud.openservices.tablestore.timeline.TimelineMetaStore;
import com.alicloud.openservices.tablestore.timeline.TimelineQueue;
import com.alicloud.openservices.tablestore.timeline.TimelineStore;
import com.alicloud.openservices.tablestore.timeline.TimelineStoreFactory;
import com.alicloud.openservices.tablestore.timeline.core.TimelineStoreFactoryImpl;
import com.alicloud.openservices.tablestore.timeline.model.*;
import com.alicloud.openservices.tablestore.timeline.query.FieldCondition;
import com.alicloud.openservices.tablestore.timeline.query.ScanParameter;
import com.alicloud.openservices.tablestore.timeline.query.SearchParameter;
import com.alicloud.openservices.tablestore.timeline.query.SearchResult;
import org.angelica.aliyun.entity.StsToken;

import java.util.Iterator;
import java.util.List;

/**
 * IM消息服务
 * @author aizhimin
 */
public class TimelineImManager {

    private static TimelineImManager instance = new TimelineImManager();
    private TimelineImManager(){ }

    private static StsToken tbStsToken = null;
    private static SyncClient syncClient = null;
    public static TimelineImManager getInstance(String endpoint, String instanceName, StsToken stsToken){
        //token过期，则重新创建SyncClient
        if(tbStsToken != null && !tbStsToken.getSecurityToken().equals(stsToken.getSecurityToken())){
            if(syncClient != null){
                syncClient.shutdown();
            }
            syncClient = null;
        }

        if(syncClient == null) {
            tbStsToken = stsToken;
            // ClientConfiguration提供了很多配置项，以下只列举部分。
            ClientConfiguration clientConfiguration = new ClientConfiguration();
            // 设置建立连接的超时时间。
            clientConfiguration.setConnectionTimeoutInMillisecond(10000);
            // 设置socket超时时间。
            clientConfiguration.setSocketTimeoutInMillisecond(10000);
            // 设置重试策略，若不设置，采用默认的重试策略。
            clientConfiguration.setRetryStrategy(new AlwaysRetryStrategy());
            syncClient = new SyncClient(endpoint, stsToken.getAccessKeyId(), stsToken.getAccessKeySecret(),
                    instanceName,clientConfiguration,stsToken.getSecurityToken());
        }
        return instance;
    }

    public TimelineStoreFactory getTimelineStoreFactory(){
        return new TimelineStoreFactoryImpl(syncClient);
    }

    /**
     * 创建一个新的群聊
     * @param groupMetaStore
     * @param groupName
     * @return
     */
    public TimelineMeta createNewGroup(TimelineMetaStore groupMetaStore,String timelineId, String groupName) {
        TimelineMeta group = new TimelineMeta(new TimelineIdentifier.Builder().addField("timeline_id", timelineId).build())
                .setField("group_name", groupName)
                .setField("create_time", System.currentTimeMillis());
        groupMetaStore.insert(group);
        return group;
    }

    public SearchResult<TimelineMeta> searchGroupMeta(TimelineMetaStore groupMetaStore, String text) {
        FieldCondition condition =
                FieldCondition.or(
                        FieldCondition.field("group_name").equals(text)
                );
        SearchParameter param = new SearchParameter(condition)
                .limit(10)
                .calculateTotalCount();
        return groupMetaStore.search(param);
    }

    public TimelineMeta readGroupMeta(TimelineMetaStore groupMetaStore, String timelineId) {
        return groupMetaStore.read(new TimelineIdentifier.Builder().addField("timeline_id", timelineId).build());
    }

    public String sendGroupMessage(TimelineStore storeService, TimelineStore syncService,
                                 TimelineMeta groupMeta, String[] groupMembers,
                                 String messageId, String message) {
        long sendTime = System.currentTimeMillis();
        TimelineMessage tm = new TimelineMessage()
                .setField("text", message)
                .setField("timestamp", sendTime)
                .setField("message_id", messageId)
                .setField("receivers", groupMembers);

        // store message to group's send box, and set sequence id manually
        TimelineQueue groupTimelineQueue = storeService.createTimelineQueue(groupMeta.getIdentifier());
        groupTimelineQueue.store(tm);

        // sync message to user's receive box
        String timelineId = groupMeta.getIdentifier().getField(0).getValue().asString();
        TimelineMessage tm2 = new TimelineMessage()
                .setField("text", message)
                .setField("timestamp", sendTime)
                .setField("message_id", messageId);
        for (String user : groupMembers) {
            TimelineQueue userTimelineQueue = syncService.createTimelineQueue(
                    new TimelineIdentifier.Builder().addField("timeline_id", timelineId + "_" + user).build());
            userTimelineQueue.store(tm2);
        }

        return messageId;
    }


    public List<SearchResult.Entry<TimelineEntry>> searchMessage(TimelineStore storeService, String user, String text) {
        FieldCondition condition =
                FieldCondition.and(
                        FieldCondition.field("receivers").equals(user),
                        FieldCondition.field("text").match(text)
                );

        SearchParameter searchParameter = new SearchParameter(condition).limit(100).calculateTotalCount().
                orderBy(new String[]{"timestamp"}, SortOrder.DESC);
        SearchResult<TimelineEntry> result = storeService.search(searchParameter);

        return result.getEntries();

    }

    public long receiveNewMessages(TimelineStore syncService, String user, long seqId) {
        TimelineQueue receiveBox = syncService.createTimelineQueue(new TimelineIdentifier.Builder().addField("timeline_id", user).build());
        ScanParameter param = new ScanParameter().scanForward(seqId).maxCount(100);
        Iterator<TimelineEntry> entries = receiveBox.scan(param);
        long lastSeqId = 0;
        while (entries.hasNext()) {
            TimelineEntry entry = entries.next();
            System.out.println(String.format("%d : %s", entry.getSequenceID(), entry.getMessage().getString("text")));
            lastSeqId = entry.getSequenceID();
        }

        return lastSeqId;
    }

    public TimelineMetaStore initGroupMetaTable(TimelineStoreFactory serviceFactory, boolean prepareTable) {
        // index schema of meta table, take group meta for example
        IndexSchema metaIndex = new IndexSchema();
        metaIndex.addFieldSchema(new FieldSchema("group_name", FieldType.TEXT).setIndex(true).setAnalyzer(FieldSchema.Analyzer.MaxWord));

        // set timeline schema and prepare all tables include data table/index and meta table/index.
        TimelineIdentifierSchema idSchema = new TimelineIdentifierSchema.Builder()
                .addStringField("timeline_id").build();
        TimelineMetaSchema metaSchema = new TimelineMetaSchema("im_group_meta", idSchema)
                .withIndex("metaIndex", metaIndex);
        TimelineMetaStore service = serviceFactory.createMetaStore(metaSchema);
        if (prepareTable) {
            service.prepareTables();
        }
        return service;
    }

    public TimelineStore initStoreTable(TimelineStoreFactory serviceFactory, boolean prepareTable) {
        // index schema of data table, take message full text index for example
        IndexSchema dataIndex = new IndexSchema();
        dataIndex.addFieldSchema(new FieldSchema("text", FieldType.TEXT).setIndex(true).setAnalyzer(FieldSchema.Analyzer.MaxWord));
        dataIndex.addFieldSchema(new FieldSchema("receivers", FieldType.KEYWORD).setIsArray(true).setIndex(true));
        dataIndex.addFieldSchema(new FieldSchema("timestamp", FieldType.LONG).setEnableSortAndAgg(true));

        // table used to store messages, with manual set sequence id, long term storage and index.
        TimelineIdentifierSchema idSchema = new TimelineIdentifierSchema.Builder()
                .addStringField("timeline_id").build();
        TimelineSchema timelineSchema = new TimelineSchema("im_store_table", idSchema)
                .manualSetSeqId()
                .withIndex("dataIndex", dataIndex);
        TimelineStore service = serviceFactory.createTimelineStore(timelineSchema);
        if (prepareTable) {
            service.prepareTables();
        }
        return service;
    }

    public TimelineStore initSyncTable(TimelineStoreFactory serviceFactory, boolean prepareTable) {
        // table used for sync messages, with auto generated sequence id and ttl.
        TimelineIdentifierSchema idSchema = new TimelineIdentifierSchema.Builder()
                .addStringField("timeline_id").build();
        TimelineSchema timelineSchema = new TimelineSchema("im_sync_table", idSchema)
                .autoGenerateSeqId()
                .setTimeToLive(86400 * 7);

        TimelineStore service = serviceFactory.createTimelineStore(timelineSchema);
        if (prepareTable) {
            service.prepareTables();
        }
        return service;
    }

}
